#include<stdio.h>
#include<malloc.h>

typedef struct queue Queue;

struct queue{
	int front, rear, capacity;
	int *arr;
	char full;
};

Queue *CreateQueue(int);
int EnQueue(Queue *, int);
int DeQueue(Queue *);
int isQueueEmpty(Queue *);
int isQueueFull(Queue *);
int QueueSize(Queue *);

int main(){
	int i = 0, data;
	Queue *q = CreateQueue(5);

	for(i=0;i<6;i++){
		if(EnQueue(q, (i + 1))){
			printf("\nValue %d enqueued successfully!", i);
			printf("\nCurrent Queue Size: %d", QueueSize(q));
		}else{
			printf("\nValue %d enqueue failed!", i);
		}
	}
	DeQueue(q);
	EnQueue(q, 36);
	printf("\n--Debug--\nPrinting complete array\n");
	for(i=0;i<q->capacity;i++){
		printf("\nValue at arr[%d] is: %d", i, q->arr[i]);
	}
	
	printf("\n--Pulling stuff out!--");
	for(i=0;i<6;i++){
		if(data = DeQueue(q)){
			printf("\nFetched value %d from head of Queue!", data);
			printf("\nCurrent Queue Size: %d", QueueSize(q));
		}else{
			printf("\nFetch failed! Queue Empty?:%s and array size is: %d", (isQueueEmpty(q)?"True":"False"), QueueSize(q));
		}
	}	


	printf("\n--Queue--\n");
	return 0;
}

Queue *CreateQueue(int cap){
	Queue *Q = (Queue *)malloc(sizeof(Queue));
	if(!Q){
		return NULL;
	}
	Q->front = 0;
	Q->rear = 0;
	Q->capacity = cap;
	Q->full = 0;
	Q->arr = (int *)malloc(cap * sizeof(int));
	if(!Q->arr){
		free(Q);
		return NULL;
	}
	return Q;
}

int EnQueue(Queue *Q, int data){
	if(isQueueFull(Q)){
		return 0;
	}else{
		Q->arr[Q->rear] = data;
		Q->rear = (Q->rear + 1) % Q->capacity;
		if(Q->front == Q->rear){
			Q->full = 1;
		}
		return 1;
	}
}

int DeQueue(Queue *Q){
	if(isQueueEmpty(Q)){
		return 0;
	}else{
		int data = Q->arr[Q->front];
		Q->front = (Q->front + 1) % Q->capacity;
		if(Q->full){
			Q->full = 0;
		}
		return data;
	}
}

int isQueueEmpty(Queue *Q){
	printf("\nQueue front: %d and Queue rear: %d", Q->front, Q->rear);
	return (Q->front == Q->rear && (1 - (int)Q->full));	//return empty true when the rear and front coincide and queue full is not set
}

int isQueueFull(Queue *Q){
	return Q->full;
		
}
int QueueSize(Queue *Q){
	return ((Q->rear >= Q->front)?(Q->rear - Q->front):(Q->capacity - (Q->front - Q->rear)));
}

